var R = {
  drawable: {explosion: new Image,
             ufo: new Image,
             rocket: new Image,
             me: new Image},
  shapes: {star: [{x:0,y:0},
		  {x:1.27595,y:-2.58545},
		  {x:4.12915,y:-3},
		  {x:2.06455,y:-5.0125},
		  {x:2.55195,y:-7.85415},
		  {x:0,y:-6.5125},
		  {x:-2.552,y:-7.85415},
		  {x:-2.0646,y:-5.0125},
		  {x:-4.1292,y:-3},
		  {x:-1.276,y:-2.58545}
		 ]},
  init: function (callback) {
    for (var k in R.drawable) {
      R.drawable[k].src = 'pix/' + k + '.png';
      R.drawable[k].onload = callback;
    }
  },
  length: function () {
    var i = 0;
    for (var k in R.drawable) {
      ++i;
    }
    return i;
  }
};
var Cwar = {
  createCanvas: function(container) {
    var canvas = new Element('canvas');
    canvas.writeAttribute('width', container.getWidth());
    canvas.writeAttribute('height', container.getHeight());
    container.insert(canvas);
    return {elm: canvas, ctx: canvas.getContext('2d')};
  },
  Loader: Class.create({
    callback: null,
    canvas: null,
    height: 78,
    img: new Image,
    nb_elm: 0,
    step: 0,
    width: 243,
    initialize: function (container, exec, callback, nb_elm) {
      this.canvas = Cwar.createCanvas(container);
      this.canvas.ctx.fillStyle = 'black';
      this.canvas.ctx.fillRect(0, 0, this.canvas.elm.width, this.canvas.elm.height);
      this.img.src = 'pix/me.png';
      this.img.onload = function() {
	this.canvas.ctx.drawImage(this.img, this.canvas.elm.width / 2 - this.width / 2, this.canvas.elm.height/ 2 - this.height / 2);
	eval(exec);
      }.bind(this);
      this.nb_elm = nb_elm;
      this.callback = callback;
    },
    update: function (event) {
      this.step++;
      this.canvas.ctx.fillStyle = '#209600';
      this.canvas.ctx.fillRect(this.canvas.elm.width / 2 - this.width / 2 + 4, this.canvas.elm.height / 2 + this.height / 2 + 51, 239 * this.step / this.nb_elm, 26);
      if (this.step == this.nb_elm) {
        this.callback();
      }
    }
  }),
  Game: Class.create({
    background: null,
    container: null,
    ctx: null,
    height: null,
    level: null,
    level_nb: 0,
    overlay: null,
    player: null,
    width: null,
    initialize: function (container) {
      this.container = container;
      this.container.update('');
      this.canvas = Cwar.createCanvas(this.container);
      this.width = this.container.getWidth();
      this.height = this.container.getHeight();
      this.canvas.ctx.lineWidth = 1;
      this.background = new Cwar.Background(this);
      this.player = new Cwar.Player(this);
      this.overlay = new Element('p');
      this.overlay.addClassName('title');
      this.container.insert({bottom: this.overlay});
      this.nextLevel();
    },
    nextLevel: function() {
      this.level = new Cwar.Level(this, this.player, ++this.level_nb);
    }
  }),
  Player: Class.create({
    canvas: null,
    level: null,
    game: null,
    height: 64,
    img: R.drawable.me,
    level: null,
    life: 100,
    move_listener: null,
    rockets: [],
    score: 0,
    score_elm: null,
    width: 48,
    x: null,
    y: null,
    initialize: function (game) {
      this.game = game;
      this.canvas = Cwar.createCanvas(game.canvas.elm.parentNode);
      this.score_elm = new Element('span');
      this.score_elm.addClassName('score');
      game.canvas.elm.parentNode.insert({bottom: this.score_elm.update('score: ' + this.score)});
      this.x = (game.canvas.elm.width / 2);
      this.y = game.canvas.elm.height - this.height - 5;
      this.draw();
      this.move_listener = this.move.bind(this);
      this.decrementLife({force: 0});
      document.observe('keydown', this.move_listener);
    },
    decrementLife: function (rocket) {
      this.canvas.ctx.clearRect(0, 0, this.life + 1, 16);
      this.life -= rocket.force;
      if (this.life <= 0) {
	this.game.level.gameOver();
      }
      var lineargradient = this.canvas.ctx.createLinearGradient(0, 0, 0, 15);
      lineargradient.addColorStop(1,'white');
      lineargradient.addColorStop(0.5,'#DDD');
      lineargradient.addColorStop(0,'black');
      this.canvas.ctx.fillStyle = lineargradient;
      this.canvas.ctx.strokeStyle = 'white';
      this.canvas.ctx.fillRect(0, 0, this.life, 15);
      this.canvas.ctx.strokeRect(1, 1, this.life - 1, 14);
    },
    destroy: function() {
      this.rockets.invoke('destroy');
      this.canvas.ctx.clearRect(this.x, this.y, this.width, this.height);
      document.stopObserving(this.move_listener);
    },
    draw: function () {
      this.canvas.ctx.save();
      this.canvas.ctx.drawImage(this.img, this.x, this.y);
      this.canvas.ctx.restore();
    },
    launchRocket: function () {
      this.rockets.push(new Cwar.Rocket(this, this.game.level.enemies, 180));
    },
    move: function (event) {
      var x = this.x;
      var y = this.y;
      var step = 10;
      if (event.keyCode == Event.KEY_LEFT) {
        if (x - step <= 0) {
          return;
        }
        x -= step;
      } else if (event.keyCode == Event.KEY_RIGHT) {
        if (x + step + this.width >= this.canvas.elm.width) {
          return;
        }
        x += step;
      } else if (event.keyCode == Event.KEY_DOWN) {
        if (y + step + this.height >= this.canvas.elm.height) {
          return;
        }
        y += step;
      } else if (event.keyCode == Event.KEY_UP) {
        if (y - step <= 0) {
          return;
        }
        y -= step;
      } else if (event.keyCode == 32) {
        this.launchRocket();
        return;
      } else {
        return;
      }
      this.canvas.ctx.clearRect(this.x, this.y, this.width, this.height);
      this.x = x;
      this.y = y;
      this.draw();
    },
    removeRocket: function (rocket) {
      delete this.rockets[rocket.id];
    },
    updateScore: function (enemy, rocket) {
      this.score += enemy.points;
      this.score_elm.update('score: ' + this.score);
    }
  }),
  Rocket: Class.create({
    canvas: null,
    direction: -1,
    force: 10,
    id: null,
    img: R.drawable.rocket,
    last_y: null,
    length: 24,
    owner: null,
    step: 15,
    targets: [],
    pe: null,
    width: 7,
    x: null,
    y: null,
    initialize: function (owner, targets, direction) {
      if (!Cwar.Rocket.canvas) {
        Cwar.Rocket.canvas = Cwar.createCanvas(owner.canvas.elm.parentNode);
      }
      this.canvas = Cwar.Rocket.canvas;
      this.id = owner.rockets.length + 1;
      this.owner = owner;
      this.targets = targets || [];
      this.direction = direction > 0 ? direction : 190;
      if (this.direction == 190) {
	this.y = owner.y + owner.height + this.length;
	this.x = owner.x + owner.width/2 + this.width/2;
      } else {
	this.x = owner.x + owner.width/2 - this.width/2;
	this.y = owner.y - this.length;
      }
      this.last_y = this.y;
      this.canvas.ctx.strokeStyle = 'white';
      this.pe = new PeriodicalExecuter(this.draw.bind(this), 0.03);
    },
    checkImpact: function(enemy) {
      if (this.x > enemy.x && this.x < enemy.x + enemy.width
         && this.y > enemy.y && this.y < enemy.y + enemy.height) {
	this.owner.updateScore(enemy, this);
        this.owner.level.remove(enemy, this);
        new Cwar.Explosion(this.canvas, this.x + this.width / 2, this.y);
        this.destroy();
      }
    },
    clearPostion: function () {
      var direction_y = this.direction == 190 ? -1 : 1;
      var direction_x = this.direction == 190 ? -2.5 : -0.5;
      this.canvas.ctx.clearRect(this.x + (this.width/2) * direction_x, this.last_y, this.width * 2, (this.length + this.step) * direction_y);
    },
    destroy: function() {
      this.pe.stop();
      this.clearPostion();
      this.owner.removeRocket(this);
      this.y = -this.step;
    },
    draw: function () {
      this.clearPostion();
      this.targets.each(this.checkImpact.bind(this));
      if (!this.pe.currentlyExecuting) {
        return;
      }
      if (this.y <= -this.step) {
        this.destroy();
        return;
      }
      if (this.direction == 190) {
	this.canvas.ctx.save();
	this.canvas.ctx.translate(this.x, this.y);
	this.canvas.ctx.rotate(Math.PI);
	this.canvas.ctx.drawImage(this.img, 0, 0);
	this.canvas.ctx.restore();
      } else {
	this.canvas.ctx.drawImage(this.img, this.x, this.y);
      }
      this.last_y = this.y;
      this.y += this.step * Math.sin(this.direction);
    }
  }),
  Explosion: Class.create({
    canvas: null,
    height:64,
    img: R.drawable.explosion,
    pe: null,
    step: 0,
    width:64,
    x: 0,
    y: 0,
    initialize: function(canvas, x, y) {
      if (!Cwar.Explosion.canvas) {
        Cwar.Explosion.canvas = Cwar.createCanvas(canvas.elm.parentNode);
      }
      this.canvas = Cwar.Explosion.canvas;
      this.x = x - this.width / 2;
      this.y = y - this.height / 2;
      this.pe = new PeriodicalExecuter(this.nextStep.bind(this).curry(canvas), 0.06);
    },
    draw: function(canvas) {
      this.canvas.ctx.save();
      this.canvas.ctx.beginPath();
      this.canvas.ctx.moveTo(this.x, this.y);
      this.canvas.ctx.lineTo(this.x + this.width, this.y);
      this.canvas.ctx.lineTo(this.x + this.width, this.y + this.height - 10);
      this.canvas.ctx.lineTo(this.x, this.y + this.height - 10);
      this.canvas.ctx.closePath();
      this.canvas.ctx.clip();
      this.canvas.ctx.drawImage(this.img, this.x - this.width * (this.step%4), this.y - this.height * Math.floor(this.step/4));
      this.canvas.ctx.restore();
    },
    nextStep: function(canvas) {
      this.draw(canvas);
      this.step++;
      if (this.step >= 13) {
	this.canvas.ctx.clearRect(this.x, this.y, this.width, this.height);
        this.pe.stop();
      }
    }
  }),
  Background: Class.create({
    canvas: null,
    max_stars: 50,
    pe: null,
    stars: [],
    step: 10,
    initialize: function (game) {
      this.canvas = Cwar.createCanvas(game.canvas.elm.parentNode);
      this.width = this.canvas.elm.width;
      this.height = this.canvas.elm.height;
      for (var i = 0; i < this.max_stars; ++i) {
        this.stars.push({color: '200',
                         x: Math.round(Math.random()*this.width),
                         y: Math.round(Math.random()*this.height),
                         size: Math.random() + 1.5,
                         speed: Math.random()
                        });
      }
      this.nextStep();
    },
    drawStar: function(star) {
      this.canvas.ctx.fillStyle = "rgb(" + star.color + "," + star.color + "," + star.color + ")";
      this.canvas.ctx.beginPath();
      this.canvas.ctx.moveTo(star.x, star.y);
      for(var i=0; i < R.shapes.star.length; i++){
        var coord = R.shapes.star[i];
        this.canvas.ctx.lineTo(star.x - coord.x / star.size, star.y - coord.y / star.size);
      }
      this.canvas.ctx.fill();
    },
    moveAndDisplayStar: function(star) {
      star.y += this.step * star.speed;
      if (star.y > this.height) {
        star.y -= this.height;
        star.x = Math.round(Math.random()*this.width);
      }
      this.drawStar(star);
    },
    nextStep: function() {
      this.canvas.ctx.clearRect(0, 0, this.width, this.height);
      this.stars.each(this.moveAndDisplayStar.bind(this));
      if (this.pe === null) {
        this.pe = new PeriodicalExecuter(this.nextStep.bind(this), 0.05);
      }
    }
  }),
  Level: Class.create({
    canvas: null,
    enemies: [],
    game: null,
    nb_enemies: 1,
    pe: null,
    player: null,
    initialize: function(game, player, lvl_number) {
      this.canvas = Cwar.createCanvas(game.canvas.elm.parentNode);
      this.player = player;
      this.player.level = this;
      this.nb_enemies = lvl_number%10;
      for (var i = 0; i < this.nb_enemies; i++) {
        this.enemies.push(new Cwar.Enemy(this, Math.floor(lvl_number/10)));
      }
      this.game = game;
      this.game.overlay.update('Level ' + lvl_number);
      this.game.overlay.setStyle({ display: 'block', opacity:1, width:'100%', height:'12px' });
      Effect.Puff(this.game.overlay);
      this.nextStep();
    },
    gameOver: function () {
      this.pe.stop();
      this.enemies.invoke('destroy');
      for (var k in this.enemies) {
        delete this.enemies[k];
      }
      this.canvas.ctx.clearRect(0, 0, this.canvas.elm.width, this.canvas.elm.height);
      this.player.destroy();
      delete this.player;
      alert('game is over');
    },
    nextStep: function() {
      this.canvas.ctx.clearRect(0, 0, this.canvas.elm.width, this.canvas.elm.height);
      this.enemies.invoke('draw');
      if (this.pe === null) {
        this.pe = new PeriodicalExecuter(this.nextStep.bind(this), 0.08);
      }
    },
    remove: function(enemy, rocket) {
      for (var k in this.enemies) {
        if (isNaN(k)) continue;
        if (this.enemies[k] === enemy) {
          delete this.enemies[k];
          --this.nb_enemies;
	  if (this.nb_enemies < 1) {
	    this.game.nextLevel();
	  }
	  return;
        }
      }
      enemy.decrementLife(rocket);
    }
  }),
  Enemy: Class.create({
    angry_rate: 0.05,
    canvas: null,
    direction:0,
    height: 29,
    img: R.drawable.ufo,
    level: null,
    points: 10,
    rockets: [],
    step: 10,
    width: 64,
    x: 0,
    y: 0,
    initialize: function(level, ratio) {
      this.canvas = level.canvas;
      this.level = level;
      this.x = level.canvas.elm.width - (level.canvas.elm.width / 2) - (this.width / 2);
      this.direction = Math.round(Math.random()*360);
      this.step += ratio/4;
      this.points += ratio * 2;
      this.angry_rate += ratio/100;
    },
    launchRocket: function () {
      this.rockets.push(new Cwar.Rocket(this, [this.level.player]));
    },
    destroy: function () {
      this.rockets.invoke('destroy');
    },
    draw: function() {
      if (this.y < 20) {
        this.y = 20;
        this.direction = Math.round(Math.random()*180);
      }
      if (this.x < 0) {
        this.x = 0;
        this.direction = Math.round(Math.random()*180) + 90;
      }
      if (this.x > this.canvas.elm.width - this.width) {
        this.x = this.canvas.elm.width - this.width;
        this.direction = Math.round(Math.random()*180) + 180;
      }
      if (this.y > this.canvas.elm.height - this.height) {
        this.y = this.canvas.elm.height - this.height;
        this.direction = Math.round(Math.random()*180) + 270;
      }
      this.canvas.ctx.save();
      this.canvas.ctx.drawImage(this.img, this.x, this.y);
      this.canvas.ctx.restore();
      this.x += this.step * Math.cos(this.direction);
      this.y += this.step * Math.sin(this.direction);
      if (Math.random() < this.angry_rate) {
	this.launchRocket();
      }
    },
    removeRocket: function (rocket) {
      delete this.rockets[rocket.id];
    },
    updateScore: Prototype.emptyFunction
  })
};

